/**
 * dateChooser - controller for the date selector component
 *
 * Copyright (c) 2020 Cameron Beccario
 */

import {pad} from "../util/strings.js";
import * as utc from "../util/utc.js";
import * as clock from "../util/clock.js";
import {lift} from "./util.js";
import {clamp} from "../util/math.js";
import {$} from "../util/seq.js";
import {defineFocusNav, defineGridFocusNav} from "./focusNav.js";
import {defineRovingTabindexByFocus} from "./rovingTabindex.js";
import {setTabindex} from "./inert.js";
import {defineHandleButton} from "./buttons.js";
import {ø} from "../util/objects.js";
import {defineModal} from "./modal.js";

/**
 * @param container
 * @param {Model} modalStore
 * @param {string} key
 * @param {Handle} time_cursor
 * @param layerAgent
 * @param {Handle} display_timezone
 */
export function defineDateChooser(container, modalStore, key, time_cursor, layerAgent, display_timezone) {

    const state = {
        year: NaN,
        month: NaN,
        day: NaN,
        hour: NaN,
        minute: NaN,
        selectedDay: NaN,
    };

    const yearButtons = container.select("[data-name=year]").selectAll("button");
    const monthButtons = container.select("[data-name=month]").selectAll("button");
    const dayButtons = container.select("[data-name=day]").selectAll("button");
    const applyButton = container.select("[data-name=apply]");

    defineFocusNav(yearButtons);
    defineFocusNav(monthButtons);
    defineGridFocusNav(dayButtons, 7);

    defineRovingTabindexByFocus(yearButtons);
    defineRovingTabindexByFocus(monthButtons);
    defineRovingTabindexByFocus(dayButtons);

    function updateDOM() {
        const {year, month} = state;
        const firstDayOfWeek = new Date(year, month - 1, 1).getDay();
        const daysInMonth = new Date(year, month, 0).getDate();

        if (isNaN(firstDayOfWeek) || isNaN(daysInMonth)) {
            return;
        }

        state.day = clamp(state.selectedDay, 1, daysInMonth);

        dayButtons.each(lift((button, Ø, i) => {
            const offset = i - firstDayOfWeek + 1;
            const day = (offset < 1 || daysInMonth < offset) ? 0 : offset;
            button
                .text(pad(day, 2))
                .attr("data-name", day)
                .attr("aria-hidden", day === 0 ? true : null)
                .classed("cloaked", day === 0);
        }));

        yearButtons.attr("aria-selected", function() { return +this.dataset.name === state.year; });
        monthButtons.attr("aria-selected", function() { return +this.dataset.name === state.month; });
        dayButtons.attr("aria-selected", function() { return +this.dataset.name === state.day; });

        setTabindex(yearButtons, function() { return +this.dataset.name === state.year ? 0 : -1; });
        setTabindex(monthButtons, function() { return +this.dataset.name === state.month ? 0 : -1; });
        setTabindex(dayButtons, function() { return +this.dataset.name === state.day ? 0 : -1; });

        // UNDONE: don't need this if all modals focus on the dialog first
        yearButtons.classed("modal-focus", function() { return +this.dataset.name === state.year; });
    }

    yearButtons.on($`click`, function() {
        state.year = +this.dataset.name;
        updateDOM();
    });
    monthButtons.on($`click`, function() {
        state.month = +this.dataset.name;
        updateDOM();
    });
    dayButtons.on($`click`, function() {
        state.day = state.selectedDay = +this.dataset.name;
        updateDOM();
    });

    function syncExternalState() {
        const timeCursor = time_cursor.get() ?? "current";
        const parts = timeCursor === "current" ?
            (layerAgent.value()?.validTime() ?? utc.parts(clock.now())) :
            timeCursor;
        const isDeviceTZ = display_timezone.get() === "device";
        const {year, month, day, hour, minute} = isDeviceTZ ? utc.toLocalParts(parts) : utc.normalize(parts);
        Object.assign(state, {year, month, day, hour, minute, selectedDay: day});
        updateDOM();
    }

    defineHandleButton(applyButton, time_cursor, () => {
        const isDeviceTZ = display_timezone.get() === "device";
        const {year, month, day, hour, minute} = isDeviceTZ ? utc.toUTCParts(state) : state;
        return ø({year, month, day, hour, minute});
    });

    modalStore.handle(key).on($`change`, show => show && syncExternalState());
    defineModal(container, modalStore, key);
}
